package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotations.MyAutowired;
import com.lagou.edu.annotations.MyComponent;
import com.lagou.edu.annotations.MyTranstional;
import org.reflections.Reflections;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;

/**
 * @ClassName: AnnotationBeanFactory
 * @Description:
 * @Version: v1.0.0
 * @Author: chendong
 * @Date: 2020/11/25
 * Modification History:
 * Date             Author      Version     Description
 * -------------------------------------------------------------
 * 2020/11/25         chendong       v1.0.0         创建
 */

public class AnnotationBeanFactory {

    /**
     * 任务一：读取解析component注解，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    //存储对象
    private static Map<String, Object> map = new HashMap<>();


    static {
        //一：1.读取解析component注解，通过反射技术实例化对象并且存储待用（map集合）
        Reflections reflections = new Reflections("com.lagou.edu");
        Set<Class<?>> myComponentClassSet = reflections.getTypesAnnotatedWith(MyComponent.class);
        for (Class<?> aClass : myComponentClassSet) {
            try {
                //生成实例对象
                Object beanObject = aClass.newInstance();
                //获取该注解的值，即id
                MyComponent annotation = aClass.getAnnotation(MyComponent.class);
                String beanId = annotation.value();
                //判断value是否有值，没有默认一个(若果只是一个没有实现接口的类，默认首字母小写//实现接口，用接口的)
                if (StringUtils.isEmpty(beanId)) {
                    //todo
                    beanId = toLowerCaseFirstOne(aClass.getSimpleName());
                }
                //存储到 map中待用
                map.put(beanId, beanObject);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //2.读取autowired注解，反射生成实例注入父依赖
        try {
            for (Class<?> aClass : myComponentClassSet) {
                //判断该类是否有属性有autowired注解
                //获取"属性变量"上的注解的值
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(MyAutowired.class)) {
                        MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                        if (annotation.required()) {
                            //从map中获取该属性的实例
                            String childrenId = null;
                            Object children = null;
                            //判断该属性是不是接口
                            Class<?> type = field.getType();
                            if (type.isInterface()) {
                                //获取该接口的实现类的id
                                List<Class> allInterfaceAchieveClass = getAllInterfaceAchieveClass(type);
                                Class<?> childrenType = allInterfaceAchieveClass.get(0);
                                //判断是否有component注解
                                if (childrenType.isAnnotationPresent(MyComponent.class)) {
                                    String implementationClassValue = childrenType.getAnnotation(MyComponent.class).value();
                                    childrenId = StringUtils.isEmpty(implementationClassValue) ?
                                            toLowerCaseFirstOne(type.getSimpleName()) : implementationClassValue;
                                }
                            } else {
                                childrenId = field.getName();
                            }
                            children = map.get(childrenId);
                            //获取父类的id和实例
                            String parentAnnotationValue = aClass.getAnnotation(MyComponent.class).value();
                            //注解有值,注解无值则默认类名称首字母小写
                            String parentId = StringUtils.isEmpty(parentAnnotationValue) ?
                                    toLowerCaseFirstOne(aClass.getSimpleName()) : parentAnnotationValue;
                            Object parent = map.get(parentId);
                            //获取父类（component）的所有方法
                            Method[] methods = aClass.getMethods();
                            for (int j = 0; j < methods.length; j++) {
                                Method method = methods[j];
                                if (method.getName().equalsIgnoreCase("set" + childrenId)) {
                                    method.invoke(parent, children);
                                }
                            }
                            // 把处理之后的parentObject重新放到map中
                            map.put(parentId, parent);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //3.读取myTransctional注解，暂定作用在类上，将该类所有方法进行拦截添加事务相关
        //获取代理工厂的实例，id为 proxyFactory
        ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");
        for (Class<?> aClass : myComponentClassSet) {
            boolean myTransactionPresent = aClass.isAnnotationPresent(MyTranstional.class);
            if (myTransactionPresent) {
                MyComponent myComponent = aClass.getAnnotation(MyComponent.class);
                String myComponentValue = myComponent.value();
                //首先获取委托对象
                String delegateId = StringUtils.isEmpty(myComponentValue) ?
                        toLowerCaseFirstOne(aClass.getSimpleName()) : myComponentValue;
                Object delegateObject = map.get(delegateId);
                //判断该类是否实现了接口，如果实现使用jdk动态代理，没有使用cglib代理
                Class<?>[] interfaces = aClass.getInterfaces();
                if (interfaces.length > 0) {
                    //jdk动态代理
                    Object jdkProxy = proxyFactory.getJdkProxy(delegateObject);
                    map.put(delegateId, jdkProxy);
                } else {
                    //cglib代理
                    Object cglibProxy = proxyFactory.getCglibProxy(delegateObject);
                    map.put(delegateId, cglibProxy);
                }
            }
        }


    }

    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }

    //首字母转小写
    private static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }

    /**
     * 获取所有接口的实现类
     *
     * @return
     */
    public static List<Class> getAllInterfaceAchieveClass(Class clazz) {
        ArrayList<Class> list = new ArrayList<>();
        //判断是否是接口
        if (clazz.isInterface()) {
            try {
                ArrayList<Class> allClass = getAllClassByPath(clazz.getPackage().getName());
                /**
                 * 循环判断路径下的所有类是否实现了指定的接口
                 * 并且排除接口类自己
                 */
                for (int i = 0; i < allClass.size(); i++) {

                    //排除抽象类
                    if (Modifier.isAbstract(allClass.get(i).getModifiers())) {
                        continue;
                    }
                    //判断是不是同一个接口
                    if (clazz.isAssignableFrom(allClass.get(i))) {
                        if (!clazz.equals(allClass.get(i))) {
                            list.add(allClass.get(i));
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println("出现异常");
            }
        }
        return list;
    }

    /**
     * 从指定路径下获取所有类
     *
     * @return
     */
    public static ArrayList<Class> getAllClassByPath(String packagename) {
        ArrayList<Class> list = new ArrayList<>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packagename.replace('.', '/');
        try {
            ArrayList<File> fileList = new ArrayList<>();
            Enumeration<URL> enumeration = classLoader.getResources(path);
            while (enumeration.hasMoreElements()) {
                URL url = enumeration.nextElement();
                fileList.add(new File(url.getFile()));
            }
            for (int i = 0; i < fileList.size(); i++) {
                list.addAll(findClass(fileList.get(i), packagename));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 如果file是文件夹，则递归调用findClass方法，或者文件夹下的类
     * 如果file本身是类文件，则加入list中进行保存，并返回
     *
     * @param file
     * @param packagename
     * @return
     */
    private static ArrayList<Class> findClass(File file, String packagename) {
        ArrayList<Class> list = new ArrayList<>();
        if (!file.exists()) {
            return list;
        }
        File[] files = file.listFiles();
        for (File file2 : files) {
            if (file2.isDirectory()) {
                assert !file2.getName().contains(".");//添加断言用于判断
                ArrayList<Class> arrayList = findClass(file2, packagename + "." + file2.getName());
                list.addAll(arrayList);
            } else if (file2.getName().endsWith(".class")) {
                try {
                    //保存的类文件不需要后缀.class
                    list.add(Class.forName(packagename + '.' + file2.getName().substring(0,
                            file2.getName().length() - 6)));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
}